# -*- coding: utf-8 -*-
from __future__ import annotations
import os
import time
from dataclasses import dataclass
from datetime import datetime
from enum import IntEnum
from typing import List, Dict
import multiprocessing
from config.config import config

from core.dataClasses import Event
from core.securitySelection import SecuritySelection, SecuritySelectionMultiprocessing
from core.enums import DateFormat, Period, EventType
from event.event_manager import EventManager
from tactic.chan import ChanTactic, ChanTacticSelectionOption
from core import threadLocal
from core import logger
from config.securities import *

class HolderDirection(IntEnum):
    IN = 1
    OUT = 2


@dataclass
class HolderRecord:
    direction: HolderDirection
    dt: datetime

    def __str__(self):
        return f"{self.direction.name}-{self.dt.strftime(DateFormat.Y_m_d_H_M_S.value)}"


class SecurityHolder:
    def __init__(self, period: Period):
        self.period: Period = period
        self.securities: Dict[str, HolderRecord] = {}
        self.ins: List[str] = []
        self.outs: List[str] = []

    def addSecurity(self, security: str) -> SecurityHolder:
        if security in self.ins:
            return self
        elif security in self.outs:
            self.outs = self.outs - [security]
            self.ins = [security] + self.ins
            self.securities[security] = HolderRecord(HolderDirection.IN, datetime.now())
        else:
            self.ins = [security] + self.ins
            self.securities[security] = HolderRecord(HolderDirection.IN, datetime.now())
        return self

    def addSecurities(self, securities: List[str]) -> SecurityHolder:
        if securities is None or len(securities) == 0:
            return self
        for s in securities:
            self.addSecurity(s)
        return self

    def delSecurity(self, security: str) -> SecurityHolder:
        if security in self.ins:
            self.outs = [security] + self.outs
            self.ins = [s for s in self.ins if s != security]
            self.securities[security] = HolderRecord(HolderDirection.OUT, datetime.now())
        return self

    def popSecurity(self):
        security = self.ins.pop(index = 0)
        self.securities[security]= HolderRecord(HolderDirection.OUT, datetime.now())
        return security

    def __str__(self):
        s =     "\tIN        :" + ",".join([s[:-3] for s in self.ins])
        s = s + "\n"
        s = s + "\tIN  Detail:" + "\t".join([f'{s[:-3]}[{self.securities[s]}]' for s in self.ins])
        s = s + "\n"
        s = s + "\tOUT Detail:" + "\t".join([f'{s[:-3]}[{self.securities[s]}]' for s in self.outs])
        return s


def getData(lock, shareDict: Dict[str, SecurityHolder], period: Period):
    l = len(shareDict[period.name].ins)
    for i in range(l):
        yield shareDict[period.name].ins[i]


def popData(lock, shareDict: Dict[str, SecurityHolder], period: Period):
    with lock:
        if len(shareDict[period.name]) > 0:
            yield shareDict[period.name].popSecurity()


def addData(lock, shareDict: Dict[str, SecurityHolder], period: Period, security: str):
    with lock:
        if security not in shareDict[period.name]:
            shareDict[period.name] = shareDict[period.name] + [security]


class DataEventHander:
    def __init__(self, lock, shareDict: Dict[str, SecurityHolder]):
        self.lock = lock
        self.shareDict: Dict[str, SecurityHolder] = shareDict

    def __call__(self, event: Event):
        if event.type == EventType.TACTIC_MONITOR:
            with self.lock:
                if event.data.data[0] not in self.shareDict[event.data.data[1].name].ins:
                    self.shareDict[event.data.data[1].name] = self.shareDict[event.data.data[1].name].addSecurity(event.data.data[0])
        elif event.type == EventType.TACTIC_UNMONITOR:
            with self.lock:
                if event.data.data[0] in self.shareDict[event.data.data[1].name].ins:
                    self.shareDict[event.data.data[1].name] = self.shareDict[event.data.data[1].name].delSecurity(event.data.data[0])
                    v = event.data.data[1] + 1
                    while(True):
                        if v in Period._value2member_map_:
                            if Period(v).name in self.shareDict and event.data.data[0] in self.shareDict[Period(v).name].ins:
                                self.shareDict[Period(v).name] = self.shareDict[Period(v).name].delSecurity(event.data.data[0])
                            v = v + 1
                        else:
                            break



def selection(logger, lock, shareDict: Dict[str, SecurityHolder], period: Period, getFun, addFun,
              selectionOption: ChanTacticSelectionOption):
    name = "test"
    threadLocal.add("threadName", name)
    threadLocal.add("logger", logger)
    event_manager: EventManager = EventManager.getInstance(name=name)

    dataEventHander = DataEventHander(lock, shareDict)
    event_manager.addEventListener(eventType=EventType.TACTIC_MONITOR, handler=dataEventHander)
    event_manager.addEventListener(eventType=EventType.TACTIC_UNMONITOR, handler=dataEventHander)
    event_manager.start()
    chanTactic = ChanTactic()
    chanTactic.selectionOption = selectionOption
    chanTactic.setPeriod(period=period)
    securitySelection = SecuritySelectionMultiprocessing()
    securitySelection.getter = getFun
    securitySelection.lock = lock
    securitySelection.shareDict = shareDict
    securitySelection.period = period
    securitySelection.setTactic(chanTactic)
    securitySelection.__run()


def monitor(lock, shareDict: Dict[str, SecurityHolder]):
    while (True):
        logger.error(f"30分钟待操作股票列表：{str(shareDict[Period.minute30.name])}")
        logger.error(f"5分钟待操作股票列表：{str(shareDict[Period.minute5.name])}")
        time.sleep(1)


def multiMonitorDay2Min30(securities: List[str]):
    threadLocal.add("logger", logger)
    manager = multiprocessing.Manager()
    shareDict = manager.dict()
    lock = manager.Lock()
    sh = SecurityHolder(Period.day)
    sh.addSecurities(securities)
    shareDict[Period.day.name] = sh
    shareDict[Period.minute30.name] = SecurityHolder(Period.minute30)
    shareDict[Period.minute5.name] = SecurityHolder(Period.minute5)
    selectionOption_day = ChanTacticSelectionOption(trend="-", deviateStatus=1, deviateDegree=0, sucessEventType=EventType.TACTIC_MONITOR, failEventType=EventType.TACTIC_UNMONITOR, nextPeriod=Period.minute30)
    selectionOption_m30 = ChanTacticSelectionOption(trend="-", deviateStatus=1, deviateDegree=0, sucessEventType=EventType.TACTIC_MONITOR, failEventType=EventType.TACTIC_UNMONITOR, nextPeriod=Period.minute5)
    pDay = multiprocessing.Process(target=selection, args=(logger, lock, shareDict, Period.day, getData, addData, selectionOption_day))
    pM30 = multiprocessing.Process(target=selection, args=(logger, lock, shareDict, Period.minute30, getData, addData, selectionOption_m30))
    pDay.start()
    pM30.start()
    while (True):
        logger.success(f"30分钟待操作股票列表：\n{str(shareDict[Period.minute30.name])}\n5分钟待操作股票列表：\n{str(shareDict[Period.minute5.name])}")
        logger.success(f"")
        time.sleep(10)


def multiMonitorMin30(securities: List[str]):
    threadLocal.add("logger", logger)
    manager = multiprocessing.Manager()
    shareDict = manager.dict()
    lock = manager.Lock()

    sh = SecurityHolder(Period.minute30)
    sh.addSecurities(securities)
    shareDict[Period.minute30.name] = sh
    shareDict[Period.minute5.name] = SecurityHolder(Period.minute5)
    shareDict[Period.minute1.name] = SecurityHolder(Period.minute1)
    selectionOption_m30 = ChanTacticSelectionOption(trend="-", deviateStatus=1, deviateDegree=0, sucessEventType=EventType.TACTIC_MONITOR, failEventType=EventType.TACTIC_UNMONITOR, nextPeriod=Period.minute5)
    pM30 = multiprocessing.Process(target=selection, args=(logger, lock, shareDict, Period.minute30, getData, addData, selectionOption_m30))
    pM30.start()
    # selectionOption_m5  = ChanTacticSelectionOption(trend="-", deviateStatus=1, deviateDegree=0, sucessEventType=EventType.TACTIC_MONITOR, failEventType=EventType.TACTIC_UNMONITOR, nextPeriod=Period.minute1)
    # pM5  = multiprocessing.Process(target=selection, args=(logger, lock, shareDict,  Period.minute5, getData, addData, selectionOption_m5))
    # pM5.start()
    while (True):
        # logger.success(f"\n5分钟待操作股票列表：\n{str(shareDict[Period.minute5.name])}\n1分钟待操作股票列表：\n{str(shareDict[Period.minute1.name])}")
        logger.success(f"\n5分钟待操作股票列表：\n{str(shareDict[Period.minute5.name])}")
        time.sleep(10)


if __name__ == "__main__":
    securities = []

    # 上证50+深证50+沪深300
    securities = securities + sh50
    securities = securities + sz50
    securities = securities + hs300

    # 沪深300-上证50-深证50
    # securities = hs300 - sh50 - sz50

    securities = list(set(securities))
    # securities = ['603501.SH']

    # multiMonitorDay2Min30(securities)
    multiMonitorMin30(securities)